home *** CD-ROM | disk | FTP | other *** search
- unit MainForm;
-
- interface
-
- uses
- Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
- StdCtrls, ExtCtrls, ScktComp, Menus, WinCrypt;
-
- Const
- wm_ConnectSocket = wm_User+1000;
- wm_SendQueue = wm_User+1001;
- wm_DisconnectClient = wm_User+1002;
-
- type
- TIBSecMainForm = class(TForm)
- Operation: TRadioGroup;
- Log: TMemo;
- Label1: TLabel;
- SecurePort: TEdit;
- Label2: TLabel;
- Password: TEdit;
- StartStop: TButton;
- IBPort: TLabel;
- ServerSocket: TServerSocket;
- Label3: TLabel;
- ServerHost: TEdit;
- ClearLogMenu: TPopupMenu;
- ClearLog1: TMenuItem;
- LogEnabled1: TMenuItem;
- CAPIVersion: TLabel;
- CryptData: TCheckBox;
- procedure StartStopClick(Sender: TObject);
- procedure FormShow(Sender: TObject);
- procedure ServerSocketClientRead(Sender: TObject;
- Socket: TCustomWinSocket);
- procedure ServerSocketClientConnect(Sender: TObject;
- Socket: TCustomWinSocket);
- procedure ServerSocketClientDisconnect(Sender: TObject;
- Socket: TCustomWinSocket);
- procedure ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket);
- procedure OperationClick(Sender: TObject);
- procedure ClientSocketDisconnect(Sender: TObject;
- Socket: TCustomWinSocket);
- procedure ServerSocketClientError(Sender: TObject;
- Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
- var ErrorCode: Integer);
- procedure ClientSocketError(Sender: TObject; Socket: TCustomWinSocket;
- ErrorEvent: TErrorEvent; var ErrorCode: Integer);
- procedure ClientSocketConnect(Sender: TObject;
- Socket: TCustomWinSocket);
- procedure FormClose(Sender: TObject; var Action: TCloseAction);
- procedure ClearLog1Click(Sender: TObject);
- procedure LogEnabled1Click(Sender: TObject);
- private
- { Private declarations }
- CAPIProvider : TCryptProv;
- CAPIKey : TCryptKey;
- CAPIHash : TCryptHash;
- SendQueue : TThreadList;
- Procedure AcquireCAPIContext;
- Procedure CreateCAPIKey;
- Function GetCAPIVersion : String;
- Procedure Encrypt(Buffer : PChar; BufLen : Integer);
- Procedure Decrypt(Buffer : PChar; BufLen : Integer);
- Procedure LogMessage(Msg : String);
- Procedure WMConnectSocket(Var Msg : TMessage); Message wm_ConnectSocket;
- Procedure WMSendQueue(Var Msg : TMessage); Message wm_SendQueue;
- Procedure WMDisconnectClient(Var Msg : TMessage); Message wm_DisconnectClient;
- public
- { Public declarations }
- end;
-
- var
- IBSecMainForm: TIBSecMainForm;
-
- implementation
-
- {$R *.DFM}
-
- Uses WinSock;
-
- Const
- Client = 0;
- Server = 1;
-
- Type
- PDataQueueRec = ^TDataQueueRec;
- TDataQueueRec = Record
- ServerSocket : TCustomWinSocket;
- DataBuffer : PChar;
- BufferLen : Integer;
- End;
-
- Procedure TIBSecMainForm.AcquireCAPIContext;
- Begin
- If (Not CryptAcquireContext(@CAPIProvider,nil,MSDefProv,ProvRSAFull,0)) Then Begin
- { Couldn't acquire context -- try to create a new keyset (init user). }
- If (Not CryptAcquireContext(@CAPIProvider,nil,MSDefProv,ProvRSAFull,CryptNewKeySet)) Then Begin
- Raise Exception.Create('Cannot acquire context to default provider: '+
- SysErrorMessage(GetLastError));
- End;
- End;
- End;
-
- Function TIBSecMainForm.GetCAPIVersion : String;
- Var I,J : Integer;
- Begin
- J := SizeOf(Integer);
- If (Not CryptGetProvParam(CAPIProvider,PPVersion,@I,J,0)) Then Result := ''
- Else Result := 'CAPI version '+IntToStr((I shr 8) And $FF)+'.'+IntToStr(I And $FF);
- End;
-
- Procedure TIBSecMainForm.CreateCAPIKey;
- Begin
- CryptCreateHash(CAPIProvider,CAlgMD5,0,0,@CAPIHash);
- If (Not CryptHashData(CAPIHash,PChar(Password.Text),Length(Password.Text),0)) Then
- Raise Exception.Create('Cannot hash data: '+SysErrorMessage(GetLastError));
- CryptDeriveKey(CAPIProvider,CAlgRC4,CAPIHash,0,@CAPIKey);
- If (CAPIKey = 0) Then Raise Exception.Create('Cannot create cryptographic key: '+
- SysErrorMessage(GetLastError));
- End;
-
- Procedure TIBSecMainForm.Encrypt(Buffer : PChar; BufLen : Integer);
- Begin
- If (Not CryptEncrypt(CAPIKey,0,False,0,Buffer,BufLen,BufLen)) Then
- Raise Exception.Create('Cannot encrypt data: '+SysErrorMessage(GetLastError));
- End;
-
- Procedure TIBSecMainForm.Decrypt(Buffer : PChar; BufLen : Integer);
- Begin
- If (Not CryptDecrypt(CAPIKey,0,False,0,Buffer,BufLen)) Then
- Raise Exception.Create('Cannot decrypt data: '+SysErrorMessage(GetLastError));
- End;
-
- Procedure TIBSecMainForm.LogMessage(Msg : String);
- Begin
- If LogEnabled1.Checked Then Begin
- Log.Lines.Insert(0,DateTimeToStr(Now)+' ['+IntToStr(GetCurrentThreadID)+']: '+Msg+'.');
- Log.Update;
- End;
- End;
-
- procedure TIBSecMainForm.FormShow(Sender: TObject);
- begin
- SendQueue := TThreadList.Create;
- IBPort.Caption := 'InterBase port: '+IntToStr(ServerSocket.Socket.LookupService('gds_db'));
- AcquireCAPIContext;
- CreateCAPIKey;
- CAPIVersion.Caption := GetCAPIVersion;
- end;
-
- procedure TIBSecMainForm.OperationClick(Sender: TObject);
- begin
- If (Operation.ItemIndex = Client) Then Label3.Caption := 'IBSec server:'
- Else Label3.Caption := 'IB server:';
- end;
-
- procedure TIBSecMainForm.StartStopClick(Sender: TObject);
- Var Service : String;
- begin
- If (Operation.ItemIndex = Client) Then Begin
- { an InterBase client application will contact IBSec }
- ServerSocket.Port := 0;
- ServerSocket.Service := 'gds_db';
- ServerSocket.Active := True;
- Service := 'client';
- Application.Title := 'IBSec Client';
- Caption := Application.Title;
- End
- Else Begin
- { a client instance of IBSec will contact this application for secure communications }
- ServerSocket.Port := StrToInt(SecurePort.Text);
- ServerSocket.Service := '';
- ServerSocket.Active := True;
- Service := 'server';
- Application.Title := 'IBSec Server';
- Caption := Application.Title;
- End;
- { change the GUI to indicate operation }
- Operation.Enabled := False;
- ServerHost.Enabled := False;
- SecurePort.Enabled := False;
- Password.Enabled := False;
- StartStop.Enabled := False;
- CryptData.Enabled := False;
- LogMessage('Starting '+Service+' services');
- end;
-
- procedure TIBSecMainForm.ServerSocketClientConnect(Sender: TObject;
- Socket: TCustomWinSocket);
- Var CS : TClientSocket;
- begin
- LogMessage('Client '+Socket.RemoteAddress+' connect');
- Assert(Socket.Data = nil);
- CS := TClientSocket.Create(Self);
- CS.Socket.Data := Socket;
- Socket.Data := CS;
- PostMessage(Handle,wm_ConnectSocket,Integer(CS),Integer(Socket));
- end;
-
- procedure TIBSecMainForm.ServerSocketClientDisconnect(Sender: TObject;
- Socket: TCustomWinSocket);
- begin
- LogMessage('Client '+Socket.RemoteAddress+' disconnect');
- With TClientSocket(Socket.Data) do Begin
- Active := False;
- Free;
- End;
- { make sure no dangling pointers exist }
- Socket.Data := nil;
- end;
-
- procedure TIBSecMainForm.ServerSocketClientRead(Sender: TObject;
- Socket: TCustomWinSocket);
- Var
- Buffer : PChar;
- BufLen : Integer;
- DataRec : PDataQueueRec;
-
- begin
- {
- Client: the InterBase application has written data to this connection
- Server: the IBSec client has written data to this connection
- }
- BufLen := 16*1024; { 16k }
- Buffer := StrAlloc(BufLen);
- BufLen := Socket.ReceiveBuf(Buffer^,BufLen);
- If CryptData.Checked Then Begin
- If (Operation.ItemIndex = Server) Then Decrypt(Buffer,BufLen)
- Else Encrypt(Buffer,BufLen);
- End;
- LogMessage('ServerSocket.ClientRead '+IntToStr(BufLen)+' bytes');
- If ((Socket.Data = nil) Or (Not TClientSocket(Socket.Data).Socket.Connected)) Then Begin
- { save the buffer in a queue }
- New(DataRec);
- With DataRec^ do Begin
- ServerSocket := Socket;
- DataBuffer := Buffer;
- BufferLen := BufLen;
- End;
- SendQueue.Add(DataRec);
- End
- Else Begin
- { "slave" socket is ready, send data through it }
- TClientSocket(Socket.Data).Socket.SendBuf(Buffer^,BufLen);
- End;
- end;
-
- procedure TIBSecMainForm.ClientSocketRead(Sender: TObject;
- Socket: TCustomWinSocket);
- Var
- Buffer : Array[0..16384] of Char;
- BufLen : Integer;
- Sent : Integer;
-
- begin
- {
- Client: the secure IBSec server has written something to the connection
- Server: the InterBase server has written something to the connection
- }
- BufLen := Socket.ReceiveBuf(Buffer,SizeOf(Buffer));
- LogMessage('ClientSocket.Read '+IntToStr(BufLen)+' bytes');
- If CryptData.Checked Then Begin
- If (Operation.ItemIndex = Client) Then Decrypt(Buffer,BufLen)
- Else Encrypt(Buffer,BufLen);
- End;
- { send data to the other end of the socket }
- Sent := TCustomWinSocket(Socket.Data).SendBuf(Buffer,BufLen);
- If (Sent <> BufLen) Then LogMessage('Warning: could not send all bytes in ClientSocketRead');
- end;
-
- procedure TIBSecMainForm.ClientSocketConnect(Sender: TObject;
- Socket: TCustomWinSocket);
- begin
- LogMessage('ClientSocket.Connect');
- { make sure the send queue gets sent out }
- PostMessage(Handle,wm_SendQueue,0,0);
- end;
-
- procedure TIBSecMainForm.ClientSocketDisconnect(Sender: TObject;
- Socket: TCustomWinSocket);
- begin
- { disconnect the "master" connection also }
- LogMessage('ClientSocket.Disconnect');
- PostMessage(Handle,wm_DisconnectClient,0,Integer(Socket.Data));
- end;
-
- procedure TIBSecMainForm.ServerSocketClientError(Sender: TObject;
- Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
- var ErrorCode: Integer);
- begin
- LogMessage('ServerSocket.ClientError '+IntToStr(ErrorCode));
- If (ErrorCode = wsaeConnAborted) Then Begin
- LogMessage('Connection aborted, closing client');
- TClientSocket(Socket.Data).Close;
- ErrorCode := 0; { don't raise a exception }
- End;
- end;
-
- procedure TIBSecMainForm.ClientSocketError(Sender: TObject;
- Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
- var ErrorCode: Integer);
- begin
- LogMessage('ClientSocket.ClientError '+IntToStr(ErrorCode));
- end;
-
- Procedure TIBSecMainForm.WMConnectSocket(Var Msg : TMessage);
- Begin
- LogMessage('Connecting client socket');
- With TClientSocket(Msg.WParam) do Begin
- If (Operation.ItemIndex = Client) Then Port := StrToInt(SecurePort.Text)
- Else Service := 'gds_db';
- Host := ServerHost.Text;
- OnConnect := ClientSocketConnect;
- OnRead := ClientSocketRead;
- OnDisconnect := ClientSocketDisconnect;
- OnError := ClientSocketError;
- Socket.Data := Pointer(Msg.LParam);
- Active := True; { open the socket }
- End;
- End;
-
- Procedure TIBSecMainForm.WMSendQueue(Var Msg : TMessage);
- Var
- I,Bytes : Integer;
- List : TList;
- Retry : Boolean;
- Client : TClientSocket;
- DataRec : PDataQueueRec;
-
- Begin
- Retry := False;
- With SendQueue do Begin
- List := LockList;
- Try
- LogMessage('Sending queued buffers: '+IntToStr(List.Count));
- For I := 0 to List.Count-1 do Begin
- DataRec := List[I];
- With DataRec^ do Begin
- Client := ServerSocket.Data;
- If ((Client = nil) Or (Not Client.Socket.Connected)) Then Retry := True
- Else Begin
- Bytes := Client.Socket.SendBuf(DataBuffer^,BufferLen);
- LogMessage(IntToStr(Bytes)+' bytes sent');
- If (Bytes <> BufferLen) Then LogMessage('Warning: could not send all bytes in WMSendQueue');
- List[I] := nil;
- StrDispose(DataBuffer);
- Dispose(DataRec);
- End;
- End;
- End;
- List.Pack; { pack the queue }
- LogMessage('Queued buffers sent, '+IntToStr(List.Count)+' buffers still exist');
- Finally
- UnlockList;
- End;
- End;
- If Retry Then Begin
- LogMessage('Queue still holds buffers, resending them later');
- PostMessage(Handle,wm_SendQueue,0,0); { retry the queue sending again later }
- End;
- End;
-
- Procedure TIBSecMainForm.WMDisconnectClient(Var Msg : TMessage);
- Begin
- LogMessage('Handling ClientSocket.Disconnect');
- TClientSocket(Pointer(Msg.LParam)).Close;
- End;
-
- procedure TIBSecMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
- begin
- SendQueue.Free;
- CryptReleaseContext(CAPIProvider,0);
- end;
-
- procedure TIBSecMainForm.ClearLog1Click(Sender: TObject);
- begin
- Log.Clear;
- end;
-
- procedure TIBSecMainForm.LogEnabled1Click(Sender: TObject);
- begin
- LogEnabled1.Checked := Not LogEnabled1.Checked;
- end;
-
- end.
-